package com.bosicloud.cmb.ibp.aws.adapter.service.impl;

import com.alibaba.fastjson.JSON;
import com.amazonaws.services.apigateway.AmazonApiGateway;
import com.amazonaws.services.apigateway.model.*;
import com.bosicloud.cmb.ibp.aws.adapter.commom.constant.AwsConstant;
import com.bosicloud.cmb.ibp.aws.adapter.commom.dto.CreateApiResultDTO;
import com.bosicloud.cmb.ibp.aws.adapter.commom.dto.DeployApiParamDTO;
import com.bosicloud.cmb.ibp.aws.adapter.commom.dto.DeployApiResultDTO;
import com.bosicloud.cmb.ibp.aws.adapter.commom.vo.ApiKeyVO;
import com.bosicloud.cmb.ibp.aws.adapter.service.ApiGatewayService;
import com.bosicloud.cmb.ibp.aws.adapter.utils.AWSUtils;
import com.bosicloud.cmb.ibp.commom.constant.AwsApiGatewayKeyType;
import com.bosicloud.cmb.ibp.commom.properties.AWSProperties;
import com.bosicloud.cmb.ibp.commom.utils.IOUtils;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.net.URL;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

/**
 * @author Jack Peng / ziliang.peng@bosicloud.com
 * https://docs.aws.amazon.com/apigateway/latest/developerguide/stage-variables.html
 * https://docs.aws.amazon.com/cli/latest/reference/apigateway/update-method.html
 * https://docs.aws.amazon.com/AWSJavaSDK/latest/javadoc/com/amazonaws/services/apigateway/model/ImportRestApiRequest.html
 */
@Service
@Slf4j
public class ApiGatewayServiceImpl implements ApiGatewayService {

    @Autowired
    AmazonApiGateway apiGateway;
    @Autowired
    AWSProperties properties;

    @Override
    public ApiKeyVO createApiKey(Integer userId, boolean isTest) {

        CreateApiKeyRequest tt = new CreateApiKeyRequest();

        tt.setCustomerId(userId.toString());
        tt.setName(String.format("%s-%s-%s", userId, getEnvFor(isTest), UUID.randomUUID()));
        tt.setEnabled(true);
        tt.setGenerateDistinctId(true);
        CreateApiKeyResult rs = apiGateway.createApiKey(tt);
        return ApiKeyVO.builder()
                .apiKey(rs.getValue())
                .keyId(rs.getId())
                .userId(userId)
                .env(getEnvFor(isTest))
                .build();
    }

    public static String getEnvFor(boolean isTest) {
        return isTest ? "test" : "prod";
    }

    @Override
    public CreateApiResultDTO createApiGateway(String jsonUrl, boolean failOnWarnings) throws IOException {

        ImportRestApiResult importRestApiResult;

        try {
            /***, "basePath", "prepend"***/
            importRestApiResult = apiGateway.importRestApi(new ImportRestApiRequest().
                    withFailOnWarnings(failOnWarnings).
                    withParameters(ImmutableMap.of("endpointConfigurationTypes", "REGIONAL")).
                    withBody(IOUtils.getAsByteArray(new URL(jsonUrl))));
            log.debug("ImportRestApiResult \n", JSON.toJSONString(importRestApiResult, true));
        } catch (AmazonApiGatewayException e) {
            log.error("导入SwaggerAPI失败.", e);
            return CreateApiResultDTO
                    .builder()
                    .errorMessage(AWSUtils.awsException2ChineseAndPrint(e))
                    .isSuccess(false)
                    .build();
        }

        // 获取资源
        GetResourcesResult resourcesResult = apiGateway.getResources(new GetResourcesRequest().
                withRestApiId(importRestApiResult.getId()).withLimit(AwsConstant.API_GW_MAX_LIMIT));
        List<Resource> resources = resourcesResult.getItems();
        while (resourcesResult.getPosition() != null) {
            resourcesResult = apiGateway.getResources(new GetResourcesRequest().
                    withRestApiId(importRestApiResult.getId()).withLimit(AwsConstant.API_GW_MAX_LIMIT));

            if (resourcesResult.getItems().size() > 0) {
                resources.addAll(resourcesResult.getItems());
            }
        }

        for (Resource res : resources) {
            Map<String, Method> methods = res.getResourceMethods();
            log.debug("AWS APIGateway Resource\n{}", JSON.toJSONString(res, true));

            if (null == methods) {
                continue;
            }

            for (String key : methods.keySet()) {
                log.debug("Modify API Endpoint.\nSet Endpoint:{} \nKey:{}", AwsConstant.ENDPOINT_HOLD_URL + res.getPath(), key);
                try {
                    apiGateway.putIntegration(new PutIntegrationRequest().
                            withType(IntegrationType.HTTP_PROXY).
                            withHttpMethod(key).
                            withIntegrationHttpMethod(key).
                            withResourceId(res.getId()).
                            withUri("http://" + AwsConstant.ENDPOINT_HOLD_URL + res.getPath()).
                            withRestApiId(importRestApiResult.getId()));

                    apiGateway.updateMethod(new UpdateMethodRequest().
                            withRestApiId(importRestApiResult.getId()).
                            withHttpMethod(key).
                            withPatchOperations(new PatchOperation().
                                    withOp("replace").
                                    withPath("/apiKeyRequired").
                                    withValue("true")).
                            withResourceId(res.getId()));
                } catch (AmazonApiGatewayException e) {
                    return CreateApiResultDTO
                            .builder()
                            .errorMessage(AWSUtils.awsException2ChineseAndPrint(e))
                            .isSuccess(false)
                            .build();
                }
            }
        }

        return CreateApiResultDTO
                .builder()
                .keyId(importRestApiResult.getId())
                .isSuccess(true)
                .build();
    }

    @Override
    public DeployApiResultDTO deployApi(DeployApiParamDTO deployApi) {

        // 创建部署
        CreateDeploymentResult result = apiGateway.createDeployment(new CreateDeploymentRequest().
                withRestApiId(deployApi.getAwsApiId()).
                withStageName(deployApi.getStageName()).
                withVariables(ImmutableMap.of(AwsConstant.STAGE_VARIABLES_URL, deployApi.getEndpointUrl())));
        log.debug("CreateDeploymentResult \n{}", JSON.toJSONString(result, true));

        GetStageResult stageResult = apiGateway.getStage(new GetStageRequest().
                withRestApiId(deployApi.getAwsApiId()).
                withStageName(deployApi.getStageName()));
        log.debug("GetStageResult \n {}", JSON.toJSONString(stageResult, true));

        // 创建使用计划 ApiId = UsagePlanName
        CreateUsagePlanResult usagePlanResult = apiGateway.createUsagePlan(new CreateUsagePlanRequest().
                withName("UP-" + deployApi.getStageName() + "-" + deployApi.getApiId()).
                withApiStages(new ApiStage().
                        withApiId(deployApi.getAwsApiId()).
                        withStage(deployApi.getStageName())));

        // 使用计划与用户的Key绑定
        String usagePlanId = usagePlanResult.getId();
        apiGateway.createUsagePlanKey(new CreateUsagePlanKeyRequest().
                withUsagePlanId(usagePlanId).
                withKeyId(deployApi.getKeyId()).
                withKeyType(AwsApiGatewayKeyType.API_KEY));

        return DeployApiResultDTO
                .builder()
                .deployId(result.getId())
                .invokeUrl(AwsConstant.getInvockUrl(deployApi.getAwsApiId(),
                        properties.getRegion().trim(),
                        deployApi.getStageName()))
                .usagePlanId(usagePlanId)
                .stage(deployApi.getStageName())
                .endpoint(deployApi.getEndpointUrl())
                .awsApiId(deployApi.getAwsApiId())
                .isSuccess(true)
                .build();
    }

    @Override
    public String subscribeApi(String keyId, String usagePlanId) {

        CreateUsagePlanKeyResult resp = apiGateway.createUsagePlanKey(new CreateUsagePlanKeyRequest().
                withUsagePlanId(usagePlanId).
                withKeyId(keyId).
                withKeyType(AwsApiGatewayKeyType.API_KEY));

        return resp.getId();
    }

    @Override
    public String unsubscribeApi(String keyId, String usagePlanId) {

        DeleteUsagePlanKeyResult resp = apiGateway.deleteUsagePlanKey(new DeleteUsagePlanKeyRequest().
                withUsagePlanId(usagePlanId).
                withKeyId(keyId));
        return resp.toString();
    }

    @Override
    public boolean deleteApiGateway(String awsApiId) {

        DeleteRestApiResult resp = apiGateway.deleteRestApi(new DeleteRestApiRequest().withRestApiId(awsApiId));
        log.info("Delete RestAPI : {}", resp);
        return true;
    }
}
